#extension GL_ARB_texture_rectangle : enable

uniform sampler2DRect random;
uniform sampler2DRect blendrandom;

uniform float tileMotarWidth;
uniform float motarStep;
uniform float motarSmooth;
uniform float motarEdgeSmooth;

uniform sampler2DRect patterns;
uniform vec2 patternDimensions[100];

uniform sampler3D zones;
uniform vec3 zoneDimensions[100];
uniform bool patternZoneClamp[100];

uniform sampler2DRect colorMaps;

uniform bool debug;
uniform float patternScale;
uniform float time;

uniform vec3 cam0;

uniform float patternBlendRandom;
uniform float zoneBlendRandom;


uniform float colorBlendRandom0;
uniform float colorBlendRandom1;
uniform float randomScale0;
uniform float randomScale1;

uniform vec3 classCenters[20];
uniform int figureIndices[20];
uniform ivec3 figureInfos[20];



/* type: 0 - pattern
         1 - zone
         2 - color
*/

uniform int classStart;
uniform int classEnd;

ivec2 classify (vec3 pos, int type) {
	
	float dMin = 999999.0;
	float d    = 999999.0;
	int found = 0;
	for (int i=classStart; i < classEnd; i++) {
		d = abs(distance(pos, classCenters[i].xyz));
		if (d<dMin) {
			dMin = d;
			found = i;
		}
	}
	return ivec2(figureInfos[figureIndices[found]][type], figureIndices[found]);
}

varying vec3 normal;
varying vec3 position;
varying vec3 localPosition;

uniform vec4 lightPosition;
uniform vec3 LightIntensity;

uniform vec3 Kd;            // Diffuse reflectivity
uniform vec3 Ka;            // Ambient reflectivity
uniform vec3 Ks;            // Specular reflectivity
uniform float Shininess;    // Specular shininess factor

vec3 ads( in vec3 iNormal){
    vec3 s = normalize(vec3(lightPosition)-position);
    vec3 v = normalize(vec3(-position));
    vec3 r = reflect( -s, iNormal );

    return LightIntensity * (
    	Ka +
        Kd * max( dot(s, iNormal), 0.0 ) +
        Ks * pow( max( dot(r,v), 0.0 ), Shininess )
    );
}

vec3 texToWorld(vec2 tex){
	vec2 tileBlends = (tex - gl_TexCoord[1].xy) / (gl_TexCoord[1].zw - gl_TexCoord[1].xy);
	int xID = int(gl_TexCoord[4].x + 0.2);
	int yID = int(gl_TexCoord[4].y + 0.2);
	int zID = int(gl_TexCoord[4].z + 0.2);
	vec3 tileWorldCoords = vec3(0.0);
	tileWorldCoords[xID] = mix(gl_TexCoord[2][xID],gl_TexCoord[3][xID], tileBlends.x);
	tileWorldCoords[yID] = mix(gl_TexCoord[2][yID],gl_TexCoord[3][yID], tileBlends.y);
	tileWorldCoords[zID] = gl_TexCoord[2][zID];
	
	return tileWorldCoords;
}

float filterwidth(vec2 v){
  vec2 fw = max(abs(dFdx(v)), abs(dFdx(v)));
  return max(fw.x, fw.y);
}


uniform float motars[20];
uniform float zoneMods[20];
uniform float zoneAdds[20];
uniform float zoneCoordScales[20];
uniform float frameRates[20];


vec4 calcTileColor(vec2 texCoord, out vec4 oMotar){
	vec2 gridCoords = floor(texCoord / (tileMotarWidth * patternScale)) + vec2(0.5,0.5);
	vec3 gridRandoms = texture2DRect(random, mod(gridCoords + vec2(0.0,100.0), vec2(200.0))).xyz;
	vec3 gridWorldCoords = texToWorld(gridCoords * (tileMotarWidth * patternScale));
	
	// get zone blend
	int pIndex = classify(gridWorldCoords + (gridRandoms * 2.0 - 1.0) * patternBlendRandom, 0).x;
	
	vec2 patternDimension = patternDimensions[pIndex];
	vec2 gridPatternCoords = mod(gridCoords, patternDimension);
	
	vec2 patternTexPosition = vec2(
		float(pIndex) * 3.0 * 50.0,
		0
	);
	
	vec4 patternInfo    = texture2DRect(patterns, patternTexPosition + gridPatternCoords);
	vec4 patternInfo2   = texture2DRect(patterns, patternTexPosition + gridPatternCoords + vec2(patternDimension.x * 2.0,0.0));
	vec4 patternIndices = texture2DRect(patterns, patternTexPosition + gridPatternCoords + vec2(patternDimension.x,      0.0));
	
	vec2 patternCoords = floor(gridCoords / patternDimension);
	vec2 tileCoords = patternIndices.xy + (patternCoords + vec2(patternIndices.z == 1.0, patternIndices.z == 2.0)) * patternDimension + vec2(0.5,0.5);
	
	//// WORLD POSITION ///////////////////////////////////
	
	vec2 tileTexCoords = tileCoords * (tileMotarWidth * patternScale);
	vec3 tileWorldCoords = texToWorld(tileTexCoords);
	
	vec3 randoms = texture2DRect(random, mod(tileCoords,  vec2(200.0))).xyz;
	
	vec3 blendRandoms0 = texture2DRect(random, mod(tileCoords*randomScale0+ vec2(100.0,0.0)*time*0.0001,  vec2(200.0))).xyz;
	vec3 blendRandoms1 = texture2DRect(random, mod(tileCoords*randomScale1+ vec2(100.0,0.0)*time*0.0001, vec2(200.0))).xyz;
	
	
	// get zone blend
	
	
	vec3 rnd = (blendRandoms0 * 2.0 - 1.0) * colorBlendRandom0 + (blendRandoms1 * 2.0 - 1.0) * colorBlendRandom1;
	ivec2 zoneInfos = classify(tileWorldCoords + rnd, 1);
	int zIndex = zoneInfos.x;
	float zoneAdd = zoneAdds[zoneInfos.y];
	float zoneMod = zoneMods[zoneInfos.y];
	float frameRate = frameRates[zoneInfos.y];
	float zoneScale = zoneCoordScales[zoneInfos.y];

	vec3 zoneDimension = zoneDimensions[zIndex];
	/*
	vec2 zoneCoords = (tileCoords + 0.0) / zoneDimension.xy * 0.01;//mod((tileCoords + 0.0) / zoneDimension.xy * 0.01, vec2(0.01));//mod(tileCoords / zoneDimension.xy * zoneScale * 0.1, vec2(0.1));
	int zone = int(texture3D(zones, vec3(zoneCoords.xy * 0.02, mod(time, zoneDimension.z) / 100.0)).x * 5.0); //0.63
	*/
	
	vec2 zoneCoords = mod(tileCoords * zoneScale, zoneDimension.xy) * 0.001;
	zoneCoords += vec2(mod(float(zIndex),10.0) * 0.1,  float(zIndex / 10) * 0.1);
	
	int zone = int(texture3D(zones, vec3(zoneCoords.xy, floor(mod(time * frameRate / 30.0, zoneDimension.z)) / 120.0)).x * 5.0); //0.63
	zone = int(mod(mod(float(zone), zoneMod) + zoneAdd, 5.0));
	
	
	// get colorBlend
	int colorClass = 0;
	float motarBright = 0.3;
	ivec2 colorInfo = classify(tileWorldCoords + rnd, 2);
	colorClass = colorInfo.x;
	motarBright = motars[colorInfo.y];
	
	
	vec4 color = texture2DRect(colorMaps, vec2(randoms.x * 100.0,zone + colorClass * 5));
	//vec4 color = vec4(1.0);
	
	///// MOTAR MODULATION ////////////////////////////////////////////////////////
		
	vec2 tileMask = texCoord / (tileMotarWidth * patternScale);
	vec2 tileBrick = floor(tileMask);
	tileMask -= tileBrick;
	vec2 wh = max(1.0 - smoothstep(1.0 - motarStep, 1.0 - motarStep + motarSmooth, tileMask), (1.0 - patternInfo.gr));// - step(1.0 - MWF,ss);
	vec2 wh2 = max(smoothstep(motarStep - motarSmooth, motarStep, tileMask), (1.0 - patternInfo2.gr));// - step(1.0 - MWF,ss);
	
	
	// OFFSET HERE
	vec2 result = gl_TexCoord[1].xy - texCoord;
	vec2 compareCoord3 = gl_TexCoord[1].xy;
	vec2 compareCoord4 = gl_TexCoord[1].zw;
	vec2 motarS = vec2(motarEdgeSmooth);
	if(result.x >= 0.0){
		compareCoord3 = gl_TexCoord[1].zw;
		compareCoord4 = gl_TexCoord[1].xy;
		motarS = vec2(motarEdgeSmooth, -motarEdgeSmooth);
	}
	if(result.y >= 0.0){
		compareCoord3 = gl_TexCoord[1].zw;
		compareCoord4 = gl_TexCoord[1].xy;
		motarS = vec2(-motarEdgeSmooth, motarEdgeSmooth);
	}
	vec2 wh3 = smoothstep(compareCoord3, compareCoord3 + motarS, texCoord);
	vec2 wh4 = smoothstep(compareCoord4, compareCoord4 - motarS, texCoord);
	
	float w = wh.x;
	float h = wh.y;
	float mColor = wh3.x * wh3.y * wh4.x * wh4.y;
	
	oMotar = vec4(w,h, wh2.xy);
	
	if(result.y >= 0.0){
		//return vec4(1.0,0.0,0.0,1.0);
	}
	//return vec4((gl_TexCoord[0].xy - 9.0) / 3.0,0.0,1.0);
	return mix(vec4(motarBright,motarBright,motarBright,1.0), color, min(w, wh2.x) * min(h,wh2.y) * mColor); //color;// 
}

uniform samplerCube cubeMap;
uniform vec3 uEyePosition;
uniform float refBlend;
uniform float refPow;

vec4 relection(in vec3 iNormal){
	vec3 incident = normalize(position - uEyePosition);
	
	vec3 t = reflect(incident, iNormal);	
	
	return textureCube(cubeMap, t);
}


uniform sampler2D randomTexture;

float noise( in vec3 x ){
    vec3 p = floor(x);
    vec3 f = fract(x);
	f = f * f * (3.0 - 2.0 * f);
	
	vec2 uv = (p.xy + vec2(37.0, 17.0) * p.z) + f.xy;
	vec2 rg = texture2D( randomTexture, (uv+ 0.5) / 256.0, -100.0 ).yx;
	return mix( rg.x, rg.y, f.z );
}

uniform int octaves;
uniform float gain;
uniform float lacunarity;
uniform float noiseScale;

float octavedNoise(in vec3 p){
	float result = 0.0;
	float myFallOff = gain;
	float myAmp = 0.0;
	p *= noiseScale;
	
	vec3 q = p ;
	for(int i = 0; i < octaves; i++){
		myAmp += myFallOff;
		result += myFallOff * noise( q ); 
		q = q * lacunarity;
		myFallOff *= gain;
	}
	
	return result / myAmp;
}

uniform float noiseAmount;

void main(){
	
	//gl_FragColor = max(vec4(float(zone)/4.0),(1.0 - (w * h)));
	// gl_FragColor =vec4(patternBlendValue);
	//if(debug)gl_FragColor = vec4(mColor,0.0,0.0, 1.0);//
	//texture2DRect(pattern0, gridCoords);//
	//gl_FragColor = vec4(tileWorldCoords / 1000.0, 1.0);
	//if(debug)gl_FragColor = vec4(float(zone) / 5.0, float(zone) / 5.0, 0.0, 1.0);
	float mip = (filterwidth(gl_TexCoord[0].xy) * 0.5) ;
	
	vec4 motar;
	vec4 colorOut;
	vec4 tileColor = calcTileColor(gl_TexCoord[0].xy + vec2(-mip,0.0), motar);
	tileColor += calcTileColor(gl_TexCoord[0].xy + vec2( mip,0.0), motar);
	tileColor += calcTileColor(gl_TexCoord[0].xy + vec2( 0.0, -mip), motar);
	tileColor += calcTileColor(gl_TexCoord[0].xy + vec2( 0.0, mip), motar);
	//calcTileColor(gl_TexCoord[0].xy, motar);
	
	float noiseValX = octavedNoise(localPosition * tileMotarWidth + 200.0);
	float noiseValY = octavedNoise(localPosition * tileMotarWidth);
	float noiseValZ = octavedNoise(localPosition * tileMotarWidth + 400.0);
	vec3 noiseDir = normalize(vec3(noiseValX, noiseValY, noiseValZ) * 2.0 -1.0);
	
	vec3 noisedNormal = mix(normal, noiseDir, noiseAmount);
	vec3 edgeNormal = normalize(
		mix(noisedNormal.yzx, noisedNormal, motar.x) + 
		mix(-noisedNormal.yzx, noisedNormal, motar.z) + 
		mix(noisedNormal.zxy, noisedNormal, motar.y) +
		mix(-noisedNormal.zxy, noisedNormal, motar.w)
	);
	
	vec4 lightColor = vec4(tileColor.rgb / 4.0 * ads(edgeNormal),1.0);// *  
	//vec4 lightColor = vec4(tileColor.rgb / 4.0 + edgeNormal,1.0);// *  
	vec4 mirrorColor = pow(relection(edgeNormal), vec4(refPow));
	
	gl_FragColor = lightColor + mirrorColor * refBlend;
	
	//gl_FragColor = vec4(motar.xy * tileColor.rg, 0.0, 1.0);
	//gl_FragColor = vec4(edgeNormal / 2.0 + 0.5, 1.0);
	if(debug){
		vec2 o = vec2(1.0);
		if(gl_TexCoord[0].x < 0.0){
			o.x = 0.0;
		}
		if(gl_TexCoord[0].y < 0.0){
			o.y = 0.0;
		}
		gl_FragColor = vec4(o,0.0,1.0);
	}
	
}